package com.jgp.security.shiro.config;

import com.jgp.security.props.SecurityConfigPropBean;
import com.jgp.security.shiro.AdminAuthorizationAttributeSourceAdvisor;
import com.jgp.security.shiro.cache.RedisSessionDAO;
import com.jgp.security.shiro.component.AdminAuthRealmBean;
import com.jgp.security.shiro.component.JGPWebSessionManager;
import com.jgp.security.shiro.filter.NormalAuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

/**
 * 项目   jgp-cloud-parent
 * 作者   loufei
 * 时间   2020-07-25
 */
@Order(500)
@Slf4j
@Import({RedisSessionDAO.class, AdminAuthRealmBean.class,AdminAuthRealmBean.class, SecurityConfigPropBean.class})
public class ShiroRedisConfiguration {
    
    @Autowired
    private SecurityConfigPropBean securityConfigPropBean;
    
    @Autowired
    private AdminAuthRealmBean adminAuthRealm;
    
    @Autowired
    private RedisSessionDAO redisSessionDAO;
    
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager adminSecurityManager = new DefaultWebSecurityManager();
        adminSecurityManager.setRealms(Arrays.asList(adminAuthRealm));
        //adminSecurityManager.setRememberMeManager(rememberMeManager());
        adminSecurityManager.setSessionManager(sessionManager());
        return adminSecurityManager;
    }
    
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new JGPWebSessionManager();
        sessionManager.setGlobalSessionTimeout(1800000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationInterval(1800000);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        redisSessionDAO.setCacheManager(new MemoryConstrainedCacheManager());
        if(Objects.nonNull(redisSessionDAO)){
            sessionManager.setSessionDAO(redisSessionDAO);
        }
        sessionManager.setSessionIdCookie(sessionCookie());
        return sessionManager;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AdminAuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }
    
    @Bean
    public SimpleCookie sessionCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("JGPSESSIONID");
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }
    
    
    @Bean
    public FilterRegistrationBean<Filter> filterRegistrationBean(DefaultWebSecurityManager securityManager) throws Exception{
        FilterRegistrationBean<Filter> filterRegistration = new FilterRegistrationBean<>();
        filterRegistration.setFilter((Filter)shiroFilter(securityManager).getObject());
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setAsyncSupported(true);
        filterRegistration.setEnabled(true);
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
        return filterRegistration;
    }
    
    @Bean
    public Authenticator authenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setRealms(Arrays.asList(adminAuthRealm));
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        return authenticator;
    }
    
    @Bean
    protected SessionStorageEvaluator sessionStorageEvaluator(){
        DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(true);
        return sessionStorageEvaluator;
    }
    
    /**
     * 设置过滤器
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filterMap = factoryBean.getFilters();
        filterMap.put("adminc", new NormalAuthenticationFilter());
        PermissionsAuthorizationFilter permissionsAuthorizationFilter = new PermissionsAuthorizationFilter();
        filterMap.put("adminp", permissionsAuthorizationFilter);
        
        String successEntry = securityConfigPropBean.getSuccessEntry();
        
        if(StringUtils.isBlank(successEntry)){
            successEntry = "/sys/main";
        }
        
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl(successEntry);
        filterMap.put("logout",logoutFilter);
        factoryBean.setFilters(filterMap);
        factoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
        return factoryBean;
    }
    
    private ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition filterChainDefinition = new DefaultShiroFilterChainDefinition();
        String [] excludes = this.securityConfigPropBean.getExcludes();
        if(Objects.nonNull(excludes)) {
            for (String exclude : excludes) {
                filterChainDefinition.addPathDefinition(exclude, "anon");
            }
        }
        
        String [] tokenCheckIncludes = this.securityConfigPropBean.getTokenCheckIncludes();
        /*if(Objects.nonNull(tokenCheckIncludes)){
            for (String tokenCheckInclude : tokenCheckIncludes) {
                filterChainDefinition.addPathDefinition(tokenCheckInclude, "authcToken,adminc,adminp");
            }
        }*/
        
        String [] includes = this.securityConfigPropBean.getIncludes();
        if(Objects.nonNull(includes)) {
            for (String include : includes) {
                filterChainDefinition.addPathDefinition(include, "adminc,adminp");
            }
        }
        
        filterChainDefinition.addPathDefinition("/security/entry-ctl/logout", "logout");
        filterChainDefinition.addPathDefinition("/security/entry-ctl/**", "anon");
        filterChainDefinition.addPathDefinition("/wfgl/unit/registerApiController/**", "anon");
        filterChainDefinition.addPathDefinition("/framework/pc/**", "anon");
        filterChainDefinition.addPathDefinition("/framework/pc2/**", "anon");
        filterChainDefinition.addPathDefinition("/download/**", "anon");
        filterChainDefinition.addPathDefinition("/public/**", "anon");
        filterChainDefinition.addPathDefinition("/static/**", "anon");
        filterChainDefinition.addPathDefinition("/jgp-ws-server/**", "noSessionCreation,anon");
        filterChainDefinition.addPathDefinition("/jgp-wxs-server/**", "noSessionCreation,anon");
        filterChainDefinition.addPathDefinition("/api/**", "authcBasic,adminc,adminp");
        filterChainDefinition.addPathDefinition("/**","adminc,adminp");
        return filterChainDefinition;
    }
}
